package com.cf.base.leak.observe;

import android.app.Activity;
import android.app.Application;
import android.os.Build;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.cf.base.leak.ObjectWatcher;
import com.cf.base.leak.callback.ActivityLifecycleCallbacks;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import static com.cf.base.leak.utils.Utils.findClass;


/**
 * @author yangji
 */
public class FragmentLeak extends ActivityLifecycleCallbacks {


    private static final String FRAGMENT_X_PACKAGE_CLASS = "androidx.fragment.app.Fragment";
    private static final String FRAGMENT_X_CLASS = "com.cf.base.leak.androidx.FragmentXLeak";
    private static final String FRAGMENT_X_CALLBACK_CLASS = "com.cf.base.leak.androidx.FragmentXCallback";

    private static final String FRAGMENT_O_PACKAGE_CLASS = "android.app.Fragment";
    private static final String FRAGMENT_O_CLASS = "com.cf.base.leak.androido.FragmentOLeak";
    private static final String FRAGMENT_O_CALLBACK_CLASS = "com.cf.base.leak.androido.FragmentOCallback";

    private List<InvokeLeakFragment> fragments;
    private final ObjectWatcher mWatcher;

    public static void install(Application application, ObjectWatcher watcher) {
        FragmentLeak leak = new FragmentLeak(watcher);
        application.registerActivityLifecycleCallbacks(leak);
    }

    private FragmentLeak(ObjectWatcher watcher) {
        this.mWatcher = watcher;
        fragments = new ArrayList<>();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            InvokeLeakFragment fragment = createFragment(FRAGMENT_O_PACKAGE_CLASS, FRAGMENT_O_CLASS, FRAGMENT_O_CALLBACK_CLASS);
            if (fragment != null) {
                fragments.add(fragment);
            }
        }
        InvokeLeakFragment fragment = createFragment(FRAGMENT_X_PACKAGE_CLASS, FRAGMENT_X_CLASS, FRAGMENT_X_CALLBACK_CLASS);
        if (fragment != null) {
            fragments.add(fragment);
        }
    }

    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        super.onActivityCreated(activity, savedInstanceState);
        invoke(activity);
    }

    //注入
    private void invoke(Activity activity) {
        fragments.forEach(invokeLeakFragment -> invokeLeakFragment.invoke(activity));
    }

    /**
     * 为了方便以后做扩展-作为 sample
     */
    @SuppressWarnings({"unchecked"})
    private InvokeLeakFragment createFragment(String packagePath, String classPath, String callbackPath) {
        if (findClass(packagePath) && findClass(classPath) && findClass(callbackPath)) {

            try {
                Class callbackClass = Class.forName(callbackPath);
                Object callBack = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{callbackClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        if (args != null && args.length == 1) {
                            mWatcher.addWatcher(args[0]);
                        }
                        return null;
                    }
                });

                Class fragmentClass = Class.forName(classPath);
                Constructor constructor = fragmentClass.getConstructor(callbackClass);
                Object fragment = constructor.newInstance(callBack);
                Method method = fragmentClass.getMethod("invoke", Activity.class);
                method.setAccessible(true);
                return new InvokeLeakFragment(fragment, method);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }


}
